
// ===============================================================================================================
// -*- C++ -*-
//
// GameLevel.cpp - Base class describing a game level.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <GameLevel.hpp>

#include <Math.hpp>
#include <Enemy.hpp>

#include <Image.hpp>
#include <GeoUtils.hpp>

#include <FireEffect.hpp>
#include <Doom 3 MD5.hpp>

#include <Weapon.hpp>
#include <Renderer.hpp>

// =========================================================
// GameLevel Class Implementation
// =========================================================

unsigned long GameLevel::AddRef(void) const
{
	return (++refCount);
}

unsigned long GameLevel::Release(void) const
{
	if (--refCount == 0)
	{
		// Time to die...
		delete this;
		return (0);
	}

	return (refCount);
}

unsigned long GameLevel::ReferenceCount(void) const
{
	return (refCount);
}

GameLevel::~GameLevel(void)
{
	unsigned int n = objects.size();

	// Cleanup:

	while (n--)
	{
		switch (objects[n].type)
		{
		case GameLevel::FLAME_FIRE:
			{
				FireEffect * fx = reinterpret_cast<FireEffect *>(objects[n].objPointer);
				delete fx;
				break;
			}
		case GameLevel::ENEMY_INSTANCE:
			{
				Enemy * enemy = reinterpret_cast<Enemy *>(objects[n].objPointer);
				delete enemy;
				break;
			}
		case GameLevel::AMMO_BOXES:
			{
				AmmoBox * ammoBoxes = reinterpret_cast<AmmoBox *>(objects[n].objPointer);
				delete[] ammoBoxes;
				break;
			}
		case GameLevel::SKY_BOX:
			{
				SkyBox * box = reinterpret_cast<SkyBox *>(objects[n].objPointer);
				delete box;
				break;
			}
		}
	}
}

// =========================================================
// LevelBuilder Class Implementation
// =========================================================

GameLevel * LevelBuilder::myLevel = 0;

bool LevelBuilder::Build(GameLevel * level)
{
	// In the lack of time I will hardcode the main level here.
	// In the future a good idea is to make it parameterized, or read the info from a file.

	if (!level)
	{
		return (false);
	}

	myLevel = level;

	// FIXME: Add more error checking!

	// Local data:
	GameLevel::Object newObj;
	int i, max_x, max_z;
	Vec3 vTmp;

	Math::SeedRandomGenerator();
	myLevel->GetExtents(max_x, max_z);

	// Add the flaming billboards to the scene //

	TexturePtr fireDistortion = Renderer::Instance()->Create2DTextureFromFile("Assets/Sprites/FireDistortion.tga");
	TexturePtr fireOpacity = Renderer::Instance()->Create2DTextureFromFile("Assets/Sprites/FireOpacity.tga");
	TexturePtr fireDiffuse = Renderer::Instance()->Create2DTextureFromFile("Assets/Sprites/FireBase.tga");

	for (i = 0; i < NUM_BILLBOARDS; ++i)
	{
		myLevel->GetRandomPos(vTmp, (max_x - 1) * max_x, (max_z - 1) * max_z);
		newObj.objPointer = new FireEffect(fireDiffuse.Get(), fireDistortion.Get(), fireOpacity.Get(), (5.0f + Math::UniformRandom() * 15.0f), vTmp);
		newObj.type = GameLevel::FLAME_FIRE;

		// One more object to the game level...
		myLevel->objects.push_back(newObj);
	}

	// Add some ammo boxes now //

	AmmoBox * ammoBoxes = new AmmoBox[NUM_AMMO_BOXES];
	Texture * boxTex = Renderer::Instance()->Create2DTextureFromFile("Assets/Sprites/Shells.tga");

	Vertex verts[24]; Triangle tris[12];
	Create3DBox(verts, tris, 5.0f, 5.0f, 5.0f);
	GLuint boxID = CreateDisplayList(verts, 24, tris, 12);

	for (i = 0; i < NUM_AMMO_BOXES; ++i)
	{
		myLevel->GetRandomPos(vTmp, (max_x - 1) * max_x, (max_z - 1) * max_z);
		vTmp.y += 3.5f;

		ammoBoxes[i] = AmmoBox(15, boxID, vTmp, boxTex); // 15 shells per box
	}

	newObj.objPointer = ammoBoxes;
	newObj.type = GameLevel::AMMO_BOXES;

	// Add the ammo boxes to the game scene:
	myLevel->objects.push_back(newObj);
	boxTex->Release();

	// Now add some enemies to break the monotony //

	for (i = 0; i < NUM_ENEMIES; ++i) // Add them to the level:
	{
		myLevel->GetRandomPos(vTmp, (max_x - 1) * max_x, (max_z - 1) * max_z);
		newObj.objPointer = new Hellknight(vTmp);
		newObj.type = GameLevel::ENEMY_INSTANCE;

		myLevel->objects.push_back(newObj);
	}

	// Add a SkyBox //

	Texture * skyBoxTextures[SKY_BOX_NUM_SIDES];
	skyBoxTextures[SKY_BOX_BOTTOM] = Renderer::Instance()->Create2DTextureFromFile("Assets/SkyBox/Side1.tga");
	skyBoxTextures[SKY_BOX_FRONT ] = Renderer::Instance()->Create2DTextureFromFile("Assets/SkyBox/Side2.tga");
	skyBoxTextures[SKY_BOX_LEFT  ] = Renderer::Instance()->Create2DTextureFromFile("Assets/SkyBox/Side3.tga");
	skyBoxTextures[SKY_BOX_BACK  ] = Renderer::Instance()->Create2DTextureFromFile("Assets/SkyBox/Side4.tga");
	skyBoxTextures[SKY_BOX_RIGHT ] = Renderer::Instance()->Create2DTextureFromFile("Assets/SkyBox/Side5.tga");
	skyBoxTextures[SKY_BOX_TOP   ] = Renderer::Instance()->Create2DTextureFromFile("Assets/SkyBox/Side6.tga");

	for (i = 0; i < SKY_BOX_NUM_SIDES; ++i)
	{
		skyBoxTextures[i]->Release();
	}

	newObj.objPointer = new SkyBox(1222, 100, -1222, 2500, 700, 2500, skyBoxTextures);
	newObj.type = GameLevel::SKY_BOX;
	myLevel->objects.push_back(newObj);

	return (true);
}

GameLevel * LevelBuilder::GetGameLevel(void)
{
	return (myLevel);
}